package com.wl.cloud.system.service.impl;

import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wl.cloud.core.dto.RestResultDTO;
import com.wl.cloud.core.utils.IpUtils;
import com.wl.cloud.security.bean.JwtProperties;
import com.wl.cloud.security.model.LoginUserDetails;
import com.wl.cloud.security.utils.JwtTokenUtil;
import com.wl.cloud.system.dao.SysUserMapper;
import com.wl.cloud.system.dao.SysUserRoleMapper;
import com.wl.cloud.system.domain.SysRole;
import com.wl.cloud.system.domain.SysUser;
import com.wl.cloud.system.domain.SysUserRole;
import com.wl.cloud.system.service.SysCommonService;
import com.wl.cloud.system.service.SysRoleService;
import com.wl.cloud.system.support.dto.LoginDTO;
import com.wl.cloud.system.support.dto.RegisterDTO;
import com.wl.cloud.system.support.enums.RoleTypeEnum;
import com.wl.cloud.system.support.utils.ConstantSystem;
import com.wl.cloud.system.support.vo.LoginVO;
import com.wl.cloud.system.support.vo.SysUserVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author: wanglin
 * @date: 2023-09-18 周一
 * @Version: 1.0
 * @Description:
 */
@Slf4j
@Service
public class SysCommonServiceImpl implements SysCommonService {

    @Autowired
    private SysUserMapper userMapper;
    @Autowired
    private SysUserRoleMapper userRoleMapper;
    @Autowired
    private SysRoleService roleService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void register(RegisterDTO dto) {
        //1- 参数验证
        Assert.isTrue(dto.getPassword().matches(ConstantSystem.PASSWORD_REG), "密码至少包含：数字,字母,字符中的两种以上，长度6-16");
        List<SysUser> userList = validateRepeat(dto.getUserId(), dto.getName());
        if (!userList.isEmpty()) {
            //2- 唯一性验证 账号,name
            userList.stream().forEach(e -> {
                Assert.isTrue(dto.getUserId().equals(e.getUserId()), "账号:" + dto.getUserId() + "已存在");
                Assert.isTrue(dto.getName().equals(e.getName()), "姓名: " + dto.getName() + "已存在");
            });
        }

        //新增用户角色，默认是普通用户
        //获取普通角色的id
        SysRole roleEntity = roleService.getRoleByCode(RoleTypeEnum.COMMON.getKey());
        Assert.notNull(roleEntity, "普通用户角色code: " + RoleTypeEnum.COMMON.getKey() + "不存在");
        //新增用户
        SysUser entity = new SysUser();
        entity.setPassword(passwordEncoder.encode(dto.getPassword()));
//        entity.setRoleType(roleEntity.getId());
//        entity.setRoleType(roleEntity.getCode());
        entity.setName(dto.getName());
        entity.setUserId(dto.getUserId());
        entity.setIsFirstLogin(true);
        entity.setId(null);
        userMapper.insert(entity);

        //新增用户角色
        SysUserRole userRole = new SysUserRole();
        userRole.setUserId(entity.getId());
        userRole.setRoleId(roleEntity.getId());
        userRoleMapper.insert(userRole);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public RestResultDTO<LoginVO> login(LoginDTO dto) {
        //登录
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserId, dto.getUsername()));
        Assert.notNull(user, "账号: " + dto.getUsername() + "不存在！");
        if (Objects.isNull(user)) {
            return RestResultDTO.newFail("账号错误！");
        }
        if (!passwordEncoder.matches(dto.getPassword(), user.getPassword())) {
            return RestResultDTO.newFail("密码错误！");
        }
        if (!user.getIsEnabled()) {
            return RestResultDTO.newFail("账号已被禁用，请联系管理员！");
        }
        SysUserVO userVo = userMapper.selectUserAndRoleByUserId(dto.getUsername());

        LoginUserDetails loginUser = new LoginUserDetails();
        loginUser.setNickname(userVo.getName());
        loginUser.setToken(IdUtil.fastUUID());
        loginUser.setUsername(userVo.getUserId());
        loginUser.setPassword(loginUser.getToken());
        loginUser.setIpaddr(IpUtils.getIpAddr());
        Set<String> roles = Arrays.asList(userVo.getRoleType().split(",")).stream().collect(Collectors.toSet());
        loginUser.setRoles(roles);
        loginUser.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(userVo.getRoleType()));

        String token = jwtTokenUtil.generateToken(loginUser);
        //登录的用户存在内存中
        jwtTokenUtil.delLoginUser(loginUser.getUsername());
        jwtTokenUtil.setLoginUser(loginUser);

        user.setIsFirstLogin(false);
        //存储最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        userMapper.updateById(user);

        //返回登录信息
        LoginVO vo = new LoginVO();
        vo.setUserId(user.getUserId());
        vo.setToken(token);
        vo.setName(user.getName());
        vo.setRoleType(userVo.getRoleType());
        vo.setLoginTime(loginUser.getLoginTime());
        vo.setExpireTime(loginUser.getExpireTime());
        vo.setRefreshTime(jwtProperties.getRefreshTime());
        log.info("sys: 用户：[" + user.getName() + "]，角色类型：[" + userVo.getRoleTypeName() + "]~~~~~~~~~~~~~登录成功");
        return RestResultDTO.newSuccess(vo, "登录成功");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void logout(HttpServletRequest request) {
        String token = jwtTokenUtil.getToken(request);
        Assert.hasText(token, "您未登录，请登录后操作！！！");
        String userName = jwtTokenUtil.getUserName(token);
        Assert.hasText(userName, "您未登录，请登录后操作！！！");
        jwtTokenUtil.delLoginUser(userName);
        log.info("sys: 用户：[" + userName + "]~~~~~~~~~~~~~退出登录");
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void resetPassword(String userId, String password) {
        Assert.hasText(password, "密码不能为空");
        Assert.isTrue(password.matches(ConstantSystem.PASSWORD_REG), "密码至少包含：数字,英文,字符中的两种以上，长度6-16");
        SysUserVO vo = userMapper.selectUserAndRoleById(userId);
        Assert.notNull(vo, "账号: " + userId + "不存在！");
        //验证操作是管理员
        boolean isAdminFlag = Arrays.asList(vo.getRoleType().split(",")).stream().anyMatch(e -> ConstantSystem.ADMIN.equals(e) || ConstantSystem.SUPER_ADMIN.equals(e));
        Assert.isTrue(isAdminFlag, "当前操作权限不足");
        SysUser userEntity = transferEntity(null, vo);
        userEntity.setPassword(passwordEncoder.encode(password));
        userMapper.updateById(userEntity);
    }

    @Transactional(readOnly = true)
    @Override
    public SysUserVO getInfo(HttpServletRequest request) {
        String token = jwtTokenUtil.getToken(request);
        LoginUserDetails loginUser = jwtTokenUtil.getLoginUserByToken(token);
        Assert.notNull(loginUser, "您未登录，请登录后操作！！！");
        SysUserVO vo = new SysUserVO();
        vo.setToken(token);
        vo.setExpireTime(loginUser.getExpireTime());
        vo.setRefreshTime(jwtProperties.getRefreshTime());
        vo.setUserId(loginUser.getUsername());
        vo.setName(loginUser.getNickname());
        vo.setRoleType(loginUser.getRoles().stream().collect(Collectors.joining(",")));
        return vo;
    }

    /**
     * 判断token是否需要刷新，登录的用户是否过期,验证当前用户有效期, 如果相差不足10分钟，自动刷新缓存
     *
     * @param request
     * @return
     */
    @Transactional(readOnly = true)
    @Override
    public RestResultDTO<LoginVO> refreshToken(HttpServletRequest request) {
        //验证请求头中是否token是否过期，若没过期，则返回新toke；否则，验证请求头中的refresh-token是否存在，若存在则返回新token，不存在返回null
        String token = jwtTokenUtil.getToken(request);
        Assert.hasText(token, "未登录，请登录");

        boolean tokenExpired = jwtTokenUtil.isTokenExpired(token);
        Assert.isTrue(!tokenExpired, "登录已过期，请重新登录");
        LoginUserDetails loginUser = jwtTokenUtil.getLoginUserByToken(token);
        Assert.notNull(loginUser, "未登录，请登录");

        LoginVO vo = new LoginVO();
        vo.setToken(jwtTokenUtil.generateToken(loginUser));
        vo.setUserId(loginUser.getUsername());
        vo.setExpireTime(loginUser.getExpireTime());
        vo.setRefreshTime(jwtProperties.getRefreshTime());
        jwtTokenUtil.verifyToken(loginUser);
        return RestResultDTO.newSuccess(vo, "操作成功");
    }

    private List<SysUser> validateRepeat(String userId, String name) {
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(StringUtils.isNotBlank(userId), SysUser::getUserId, userId)
                .eq(StringUtils.isNotBlank(name), SysUser::getName, name);
        return userMapper.selectList(queryWrapper);
    }

    private LoginVO transferVo(LoginDTO entity) {
        if (Objects.isNull(entity)) {
            return null;
        }
        LoginVO vo = new LoginVO();
        BeanUtils.copyProperties(entity, vo);
        return vo;
    }

    private SysUser transferEntity(SysUser entity, SysUserVO vo) {
        if (Objects.isNull(entity)) {
            entity = new SysUser();
        }
        BeanUtils.copyProperties(vo, entity);
        return entity;
    }
}
